﻿//-----------------------------------------------------------------------
//  This file is part of the Microsoft Code Samples.
// 
//  Copyright (C) Microsoft Corporation.  All rights reserved.
// 
//  This source code is intended only as a supplement to Microsoft
//  Development Tools and/or on-line documentation.  See these other
//  materials for detailed information regarding Microsoft code samples.
// 
//  THIS CODE AND INFORMATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
//  KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
//  IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
//  PARTICULAR PURPOSE.
//-----------------------------------------------------------------------

using System;
using System.Xml;
using System.Data;
using Microsoft.AnalysisServices.AdomdClient;
using Microsoft.AnalysisServices;
using System.Collections;
using System.Collections.Specialized;

namespace RetailAnalytic.Tools.ActivitySystem
{
    /// <summary>
    /// Summary description for ASCommands.
    /// </summary>
    [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1058:TypesShouldNotExtendCertainBaseTypes")]
    public class ASCommands : IDisposable
    {
        #region Constants
        public const string DiscoverNameConnections = "Connections";
        public const string DiscoverNameSessions = "Sessions";
        public const string DiscoverNameTransactions = "Transactions";
        public const string DiscoverNameLocks = "Locks";
        public const string DiscoverNameNone = "";

        public const string DiscoverTypeConnections = "DISCOVER_CONNECTIONS";
        public const string DiscoverTypeSessions = "DISCOVER_SESSIONS";
        public const string DiscoverTypeTransactions = "DISCOVER_TRANSACTIONS";
        public const string DiscoverTypeLocks = "DISCOVER_LOCKS";
        public const string DiscoverTypeNone = "";

        #endregion

        #region Member Variables

        private string serverName = string.Empty;
        private AdomdConnection connection = new AdomdConnection();
        private Server serverConnection;

        #endregion

        #region Constructor/Destructor

        public ASCommands(string serverName)
        {
            this.serverName = serverName;
        }

        ~ASCommands()
        {
            Dispose(false);
        }

        #endregion

        #region Public Properties

        public bool IsConnected
        {
            get
            {
                lock (this.connection)
                {
                    //return true if connection is open
                    if (this.connection != null
                        && this.connection.State == ConnectionState.Open)
                        return true;
                }
                return false;
            }
        }

        public string ServerName
        {
            get { return this.serverName; }
        }

        #endregion

        #region IDisposable

        public void Dispose()
        {
            Dispose(true);
        }

        #endregion

        #region Public Methods

        public DataTable DiscoverConnections()
        {
            //connect to Analysis Services and execute query to Discover Connections
            return this.GetDiscoverResults(DiscoverTypeConnections);
        }

        public DataTable DiscoverConnection(long connectionId)
        {
            //connect to Analysis Services and execute query to Discover Connections

            NameValueCollection col
                = new NameValueCollection(1);
            col.Add("CONNECTION_ID", connectionId.ToString(
                System.Globalization.CultureInfo.InvariantCulture));

            return this.GetDiscoverResults(DiscoverTypeConnections, col);
        }

        public DataTable DiscoverSessions()
        {
            //connect to Analysis Services and execute query to Discover Connections
            return this.GetDiscoverResults(DiscoverTypeSessions);
        }

        public DataTable DiscoverSessions(long connectionId)
        {
            //connect to Analysis Services and execute query to Discover Connections

            NameValueCollection col
                = new NameValueCollection(1);
            col.Add("SESSION_CONNECTION_ID", connectionId.ToString(
                System.Globalization.CultureInfo.InvariantCulture));

            return this.GetDiscoverResults(DiscoverTypeSessions, col);
        }

        public DataTable DiscoverSession(long sessionSpid)
        {
            //connect to Analysis Services and execute query to Discover Connections

            NameValueCollection col
                = new NameValueCollection(1);
            col.Add("SESSION_SPID", sessionSpid.ToString(
                System.Globalization.CultureInfo.InvariantCulture));

            return this.GetDiscoverResults(DiscoverTypeSessions, col);
        }

        public DataTable DiscoverTransactions()
        {
            //connect to Analysis Services and execute query to Discover Connections
            return this.GetDiscoverResults(DiscoverTypeTransactions);
        }

        public DataTable DiscoverTransactions(string sessionId)
        {
            //connect to Analysis Services and execute query to Discover Connections

            NameValueCollection col
                = new NameValueCollection(1);
            col.Add("TRANSACTION_SESSION_ID",
                @"{" + sessionId.ToString() + @"}");

            return this.GetDiscoverResults(DiscoverTypeTransactions, col);
        }

        public DataTable DiscoverLocks()
        {
            //connect to Analysis Services and execute query to Discover Connections
            return this.GetDiscoverResults(DiscoverTypeLocks);
        }

        public DataTable DiscoverLocks(long transactionId)
        {
            //connect to Analysis Services and execute query to Discover Connections

            NameValueCollection col
                = new NameValueCollection(1);
            col.Add("LOCK_TRANSACTION_ID", transactionId.ToString(
                System.Globalization.CultureInfo.InvariantCulture));

            return this.GetDiscoverResults(DiscoverTypeLocks, col);
        }

        public DataTable DiscoverLocks(string transactionId)
        {
            //connect to Analysis Services and execute query to Discover Connections

            NameValueCollection col
                = new NameValueCollection(1);
            col.Add("LOCK_TRANSACTION_ID",
                @"{" + transactionId.ToString() + @"}");

            return this.GetDiscoverResults(DiscoverTypeLocks, col);
        }

        public bool IsApplicationSession(string sessionId)
        {
            if ((this.serverConnection != null
                    && sessionId == this.serverConnection.SessionID)
                || (this.connection != null
                    && sessionId == this.connection.SessionID))
            {
                return true;
            }

            return false;
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")]
        public bool IsApplicationConnection(string connectionId)
        {
            //TBD
            //the connection objects don't support a connection Id property.
            //more work needs to be done to properly identify the connections used by the application

            //ideally we could do a DiscoverSession based on the SessionId, but this is not a supported discover.

            //one option is to do a Discover of all Sessions, locate the sessions used by this application
            //and store the associated connection Id's for reference.
            return false;
        }

        public void KillConnection(int connectionId)
        {
            this.serverConnection.CancelConnection(connectionId);
        }

        public void KillSession(string sessionId, int sessionSpid)
        {
            if (this.IsApplicationSession(sessionId))
                throw new SessionInUseException(
                    "The selected session is in use by this application and can not be cancelled.");

            this.serverConnection.CancelSession(sessionSpid);
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1801:AvoidUnusedParameters")]
        public void KillTransaction(string transactionId)
        {
            Console.WriteLine("Not implemented.");
        }

        public void Connect()
        {
            this.Connect("Data Source=" + this.serverName + "; Provider=msolap.3");
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")]
        public void Connect(string connectionString)
        {
            //establish connection to the server
            try
            {
                lock (this.connection)
                {
                    if (this.connection == null || this.connection.State != ConnectionState.Open)
                    {
                        this.serverConnection = new Server();
                        this.connection = new AdomdConnection(connectionString);
                    }

                    this.serverConnection.Connect(connectionString);
                    this.connection.Open();
                }
            }
            catch (System.Exception ex)
            {
                throw new ApplicationException("Error trying to open connection.", ex);
            }
        }

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2200:RethrowToPreserveStackDetails")]
        public void Disconnect()
        {
            //disconnect and release reference
            try
            {
                lock (this.connection)
                {
                    if (this.connection != null)
                        this.connection.Close();

                    if (this.serverConnection != null)
                        this.serverConnection.Disconnect();
                }
            }
            catch (System.Exception ex)
            {
                throw ex;
            }
        }

        #endregion

        #region Private Methods

        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
        private static DataTable ConvertXmlToDataTable(string xmlString)
        {
            DataSet ds = new DataSet();
            ds.Locale = System.Globalization.CultureInfo.InvariantCulture;
            System.IO.StringReader sr = new System.IO.StringReader(xmlString);
            XmlReader xrdr = XmlReader.Create(sr);
            ds.ReadXml(xrdr, XmlReadMode.ReadSchema);
            return ds.Tables[0];
        }

        private void Dispose(bool disposing)
        {
            if (disposing)
            {
                GC.SuppressFinalize(this);
            }

            if (this.connection != null)
            {
                try
                {
                    this.connection.Close();
                    this.connection.Dispose();
                }
                catch 
                {
                   
                }
              
            }

            if (this.serverConnection != null)
            {
                try
                {
                    this.serverConnection.Disconnect();
                    this.serverConnection.Dispose();
                }
                catch 
                {
                    
                 
                }
               
            }
        }

        private DataTable GetDiscoverResults(string requestType)
        {
            return this.GetDiscoverResults(requestType, null);
        }

        private DataTable GetDiscoverResults(string requestType, NameValueCollection restrictions)
        {
            DataSet ds = null;
            AdomdRestrictionCollection col = null;

            //build collection of restrictions for the discover
            if (restrictions != null && restrictions.Count > 0)
            {
                col = new AdomdRestrictionCollection();
                NameValueCollection.KeysCollection keys = restrictions.Keys;
                for (int i = 0; i < keys.Count; i++)
                {
                    col.Add(new AdomdRestriction(keys[i], restrictions[keys[i]]));
                }
            }

            //establish connection to the server
            try
            {
                lock (this.connection)
                {
                    if (!this.IsConnected)
                    {
                        this.Connect();
                    }
                }
            }
            catch (System.Exception ex)
            {
                throw new ApplicationException("Error trying to open connection.", ex);
            }

            //execute discover command
            try
            {
                lock (this.connection)
                {
                    ds = this.connection.GetSchemaDataSet(requestType, col);
                }
            }
            catch (System.Exception ex)
            {
                System.Diagnostics.Debug.WriteLine(ex.ToString());
                throw new ApplicationException("Error executing command on the server.", ex);
            }

            //return results of the discover
            return ds.Tables[0];
        }
        #endregion

        [Serializable]
        private class SessionInUseException : ApplicationException
        {
            public SessionInUseException(string message)
                : base(message)
            {
            }
        }
    }
}
